home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Hot Mix 17
/
Hot Mix 17.iso
/
HM17_SGI
/
research
/
lib
/
slicer.pro
< prev
next >
Wrap
Text File
|
1997-07-08
|
54KB
|
1,718 lines
; $Id: slicer.pro,v 1.25 1997/02/13 00:00:04 ali Exp $
;
; Copyright (c) 1991-1997, Research Systems, Inc. All rights reserved.
; Unauthorized reproduction prohibited.
;
; Conventions:
; Faces = faces of cube. Numbered 0 to 5:
; 0 = X = c0[0] (Corner 0)
; 1 = Y = c0[1]
; 2 = Z = c0[2]
; 3 = X = c1[0] (Corner 1)
; 4 = Y = c1[1]
; 5 = Z = c1[2]
; Orientations:
; 6 = reset to default.
; 0 = xy exchange, 1 = xz exchange, 2 = yz exchange.
; 3 = x reverse, 4 = y reverse, 5 = z reverse.
; Vertex indices: Faces
; 0 0,0,0 0,1,2
; 1 N,0,0 1,2,3
; 2 0,N,0 0,2,4
; 3 N,N,0 2,3,4
; 4 0,0,N 0,1,5
; 5 N,0,N 1,3,5
; 6 0,N,N 0,4,5
; 7 N,N,N 3,4,5
;
; Edge Index Vertex Indices
; 0 0-1
; 1 1-2
; 2 2-3
; 3 3-1
; 4 0-4
; 5 1-5
; 6 2-6
; 7 3-7
; 8 4-5
; 9 5-6
; 10 6-7
; 11 7-4
;
; Modes:
; 0 = Slices
; 1 = Cube
; 2 = Cut
; 3 = Isosurface
; 4 = Probe
; 5 = rotations
function p_inside_poly, polyv, p
; polyv = [2,n] array of polygon vertices, either clockwise or
; counter-clockwise order. Polygon must be convex.
; p = (2) point coordinates.
; return 0 if point is outside polygon, 1 if inside.
;
x = float(p[0])
y = float(p[1])
np = n_elements(polyv)/2 ;# of vertices
x1 = polyv[0,np-1] ;Start at last point
y1 = polyv[1,np-1]
pos = -1
for i=0,np-1 do BEGIN
x2 = polyv[0,i]
y2 = polyv[1,i]
k = (x*(y1-y2) + y * (x2-x1) + x1*y2-y1*x2) ;Side of line point is on
if k eq 0 THEN BEGIN ;On line?
if (y lt (y1< y2)) or (y gt (y1 > y2)) or $ ;Check more
(x lt (x1 < x2)) or (x gt (x1 > x2)) THEN return,0
ENDIF ELSE BEGIN ;Not on line
k = k gt 0 ;1 if on right side
if pos lt 0 then pos = k
if pos ne k THEN return,0
ENDELSE
x1=x2 ;Previous vertex
y1=y2
ENDFOR
return,1
end
Function p_inverse, x, y, z
;
; Given one or more screen coordinates, and their Z-buffer values
; return the object space (volume subscript) coordinates of that point.
;
; On input: x, y = scalars or vectors containing the device coordinate(s)
; z = zbuffer value(s) at [x,y]
; Result: [3,n] vector of object space (volume) coordinates.
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
; Method:
; - Convert screen coords to normalized coords.
; - Multiply by inverse of 3D transformation.
; - Scale back to data coordinates.
;
; ******* Old way *************
; ones = replicate(1, n_elements(x))
; v = [[x / float(!d.x_size)], $ ;To normalized coordinates.
; [y / float(!d.y_size)], $
; [z/65530.+0.5], $
; [ones]]
; v = ((temporary(v) # sl.pt_inverse) - (ones # [!x.s[0], !y.s[0], !z.s[0]])) / $
; (ones # [ !x.s[1], !y.s[1], !z.s[1]])
;
; **************************
; The new way folds everything into the matrix multiplication operation
; and is much faster, although its very, very, obtuse.
;
; Apply to normalized conversion to inverse matrix.
q = sl.pt_inverse * (1./[!d.x_size, !d.y_size, 65530., 1.] # replicate(1.,4))
s = 1./[!x.s[1], !y.s[1], !z.s[1], 1.0] ;To data coord scale factors
t = [!x.s[0], !y.s[0], !z.s[0], 0.0]
q = q * transpose(s # replicate(1.,4)) ; Mult inv matrix by 1/![xyz].s[1]
q[3,0] = q[3,*] - transpose(t * s) ;Add in t*s.
return, ([[x],[y],[z+32765L], [replicate(1L, n_elements(x))]] # q)[*,0:2]
end
Function slicer_plane_int, dummy
; Return the intersections of the plane with the volume cube.
; V[3,12] = intersections of plane with the 12 edges.
; Flags[12] = 1 if there is an intersection at that edge.
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
Jn = sl.orthop[0:2] ;Plane eqn
Jd = sl.orthop[3]
v = fltarr(3,12)
k = 0
for i=0,11 do begin ;Faces
p0 = sl.p0[*,sl.edges[0,i]] ;Beginning of edge
p1 = sl.p0[*,sl.edges[1,i]] ;End of edge
lv = p1-p0
t = total(lv * Jn)
if t ne 0 then begin
t = -(jd + total(p0 * Jn)) / t
p = p0 + t * lv ;Point of intersection
d = min((p - p0) * (p1-p))
if d ge 0.0 then begin ;Within edge?
v[0,k] = p ;Yes, save intersection
k = k + 1
endif
endif ;T ne 0
endfor ;i
; Sort into order:
d = max(abs(Jn), m) ;Largest plane coeff = what we ignore
u = v[[m+1, m+2] mod 3, 0:k-1] ;Get other 2 coords
a = fltarr(k) ;Angle measure
d = min(u[1,*], dmin) ;Get lowest point
u_dx = u[0,*]-u[0,dmin]
u_dy = u[1,*]-u[1,dmin]
zero_ind = Where((u_dx EQ 0.0) AND (u_dy EQ 0.0))
if (zero_ind[0] GE 0L) THEN u_dx[zero_ind] = 1.0
a=atan(u_dy, u_dx)
zero_ind=0 & u_dx=0 & u_dy=0
a[dmin] = -100. ;Anchor = first
n = sort(a) ;Go around anchor point & back
return, v[*, [n, n[0]]]
end
PRO SLICER_PLAYBACK, FILE = file, Commands
; Play back a journal. Commands are either in the designated file
; or in the string array Commands".
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
COMMON slicer_common1, old_slice, d0, z0, az, el
if n_elements(file) gt 0 THEN BEGIN ;Read from file
OPENR, unit, /GET_LUN, file, ERROR = i
if i ne 0 then begin ;OK?
widget_control, sl.file_text[1], set_value = !ERR_STRING
return
ENDIF
commands = strarr(100) ;Read up to 100 lines
i = 0
a = ''
while not eof(unit) do begin
readf, unit, a
commands[i] = a
i = i + 1
endwhile
ncommands = i
free_lun, unit
ENDIF ELSE ncommands = n_elements(commands)
IF mode ne 7 THEN BEGIN ;Not our mode?
if sl.mode_bases[mode] ne 0 THEN $ ;Remove panel if mapped
WIDGET_CONTROL, sl.mode_bases[mode], MAP=0
mode = 7 ;New mode = ours
WIDGET_CONTROL, sl.mode_bases[mode], MAP=1
ENDIF
pars = fltarr(10)
for i=0, ncommands-1 do begin ;Each command
s = strtrim(strcompress(commands[i])) ;Parse it, extracting fields
j = 0
m = 0
while j lt strlen(s) do begin ;While string left
k = strpos(s, ' ', j) ;Find next blank
if k le 0 then k = strlen(s) ;if none, go to end of string
if j eq 0 then cmd = strmid(s,0,k) $
else begin pars[m] = strmid(s,j,k) & m=m+1 & endelse
j = k+1
endwhile
WIDGET_CONTROL, sl.file_text[1], SET_VALUE = strmid(s,0,32)
case strupcase(cmd) of ;Interpret commands.....
"UNDO": slicer_undo
"ORI": BEGIN ;AXIS(3), AXIS_REVERSE(3), ROTATIONS(2)
sl.axex = pars[0:2]
sl.axrev = pars[3:5]
sl.rotation = pars[6:7]
for j=0,1 do WIDGET_CONTROL, sl.rslide[j], SET_VALUE = sl.rotation[j]
WIDGET_CONTROL, sl.rslide[2], SET_VALUE=string(pars[8]) ;Aspect
SLICER_ORIENTATION
ENDCASE
"TRANS": BEGIN ;On/Off Threshold(%)
sl.trans = pars[0]
if sl.trans eq 0 then pars[1] = 100
sl.threshold = pars[1] /100. * sl.nc3
WIDGET_CONTROL, sl.threshold_slider, SET_VALUE = pars[1]
ENDCASE
"SLICE": BEGIN ;Axis, slice_value, interp, expose, 0 for orthogonal
;Azimuth, Elev, interp, expose, 1, x0, y0, z0 for oblique
sl.interp = pars[2]
sl.expose = pars[3]
if pars[4] eq 0 then begin ;Orthogonal slices?
WIDGET_CONTROL, sl.draw_butt[sl.expose], /SET_BUTTON
SLICER_DRAW, pars[0], pars[1]
ENDIF ELSE BEGIN ;Oblique slice
az = pars[0]
el = pars[1]
s = sin(el * !DTOR)
sl.orthop[0] = sin(-az * !dtor) * s
sl.orthop[1] = cos(-az * !dtor) * s
sl.orthop[2] = cos(el * !dtor)
z0 = pars[5:7]
sl.orthop[3] = -total(sl.orthop * z0)
d0 = slicer_plane_int()
slicer_oblique
ENDELSE
ENDCASE
"COLOR": BEGIN ;Table_num (-1 = present), low, high, shading
sl.stretch = pars[1:2]
sl.shading = pars[3]/100.
for j=0,2 do WIDGET_CONTROL, sl.cslide[j], SET_VALUE = pars[j+1]
slicer_colors, pars[0]
ENDCASE
"ISO": BEGIN ;value, hi/lo
sl.isop.value = pars[0] / 100. * (sl.amax-sl.amin) + sl.amin
sl.isop.hi_lo = pars[1]
DO_ISOSURFACE
ENDCASE
"ERASE": slicer_erase
"WAIT": wait, pars[0]
"CUBE": BEGIN ;mode (1 = block, 0 = cutout), cut_ovr, interp,
; start_coords(3), end_coords(3)
mode = pars[0]
sl.cut_ovr = pars[1]
sl.interp = pars[2]
sl.p0cube = reform(pars[3:8], 3,2)
DO_CUBE
ENDCASE
ENDCASE
ENDFOR
mode=7
WIDGET_CONTROL, sl.file_text[1], SET_VALUE = 'Playback Done'
end
; Journal events if journal file is open.
PRO SLICER_JOURNAL, name, params
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
if sl.journal le 0 then return
if n_elements(params) le 0 then params = 0.
printf, sl.journal, name, params, format='(A, 1x, 10F10.3)'
end
PRO SLICER_UNDO ;Undo last operation
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
SLICER_JOURNAL, "UNDO"
if n_elements(zb_last) le 1 then return
set_plot,'Z'
tmp = tvrd(/WORDS, CHANNEL=1) ;Read depth buffer & swap
tv, zb_last, CHANNEL=1, /WORDS
zb_last = temporary(tmp)
tmp = tvrd() ;Swap them
tv, z_last
slicer_show, z_last
z_last = tmp
end
PRO slicer_orientation, i ;i = orientation
; Set the New Orientation
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
if n_elements(i) gt 0 THEN BEGIN
if i le 2 THEN BEGIN
j = 2 * i
ll = ([0,1,0,2,1,2])[j:j+1] ;axes to swap
t = sl.axex[ll[0]] & sl.axex[ll[0]] = sl.axex[ll[1]] &
sl.axex[ll[1]] = t
ENDIF ELSE if i eq 6 THEN BEGIN
sl.axex = [0,1,2] ;default transformation
sl.axrev = intarr(3)
ENDIF ELSE sl.axrev[i-3] = 1-sl.axrev[i-3] ;reverse
ENDIF
d = [ 0., dims[0], 0., dims[1], 0., dims[2]]
f = 1.0
WIDGET_CONTROL, sl.rslide[2], GET_VALUE=s
ON_IOERROR, bad_aspect
f = (float(s))[0]
if (f le 0.0) then goto, bad_aspect
IF f gt 1. THEN BEGIN
x = (f-1)/2.
d[0] = [-x * dims[0], (x+1) * dims[0], -x * dims[1], (x+1) * dims[1]]
ENDIF ELSE BEGIN
x = (1-f)/2
d[4] = [-x * dims[2], (x+1) * dims[2]]
ENDELSE
bad_aspect:
SLICER_JOURNAL, "ORI", [sl.axex, sl.axrev, sl.rotation, f ]
for i=0,2 do if sl.axrev[i] THEN BEGIN ;Swap endpoints for reversed axes
j=i*2
t = d[j] & d[j] = d[j+1] & d[j+1] = t
ENDIF
!x.type = 0 ;make sure its linear
scale3, xrange=d[0:1], yrange=d[2:3], zrange=d[4:5], ax = sl.rotation[0], $
az = sl.rotation[1]
k = 1 ;current y axis
if sl.axex[0] ne 0 THEN BEGIN ;swap x?
if sl.axex[0] eq 1 THEN BEGIN & t3d, /XYEXCH & k = 0
ENDIF ELSE t3d,/XZEXCH
ENDIF
if k ne sl.axex[1] THEN t3d,/YZEXCH
slicer_erase
if mode le 2 THEN draw_orientation
sl.pt_inverse = invert(!p.t)
end
PRO slicer_oblique ;Do an oblique slice
; Plane eqn is in sl.orthop
; d0 is the intersections of the plane with the edges of the volume
; cube.
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
COMMON slicer_common1, old_slice, d0, z0, az, el
if n_elements(d0) le 3 then return ;Anything?
SLICER_JOURNAL, "TRANS", [sl.trans, sl.threshold * 100. / sl.nc3]
SLICER_JOURNAL, "SLICE", [az, el, sl.interp, sl.expose, 1., z0 ]
widget_control, sl.pos_text, set_value = 'Extracting Oblique Slice'
set_plot,'Z'
z = (z_last = tvrd()) ;save previous image & z
zb = (zb_last = tvrd(CHANNEL=1, /WORDS))
erase
polyfill, d0, /T3D, /DATA ;Mark the slice
z1 = tvrd(CHANNEL=1, /WORDS) ;Read depth buffer
if sl.expose then points = where((z1 lt zb_last) and (z1 ne z1[0,0])) $
else points = where(z1 gt zb_last)
if points[0] lt 0 then begin
z=z_last
zb = zb_last
goto, done
endif
widget_control, sl.pos_text, set_value = $
STRING(n_elements(points), FORMAT="(i6, ' points')")
; Get voxel subscripts from screen coords:
v = p_inverse(points mod !d.x_size, points / !d.x_size, z1[points])
; Either interpolate or pick nearest neighbor
if sl.interp THEN v = interpolate(a, v[*,0], v[*,1], v[*,2]) $
else v = a[long(temporary(v)) # [1, dims[0], dims[0] * dims[1]]]
v = bytscl(temporary(v), max=sl.amax, min=sl.amin, top = sl.nc3-1) ;face data
if sl.trans THEN BEGIN ;Transparency? Remove those under thresh.
good = where(v ge sl.threshold)
v = v[good]
points = points[good]
endif
dummy = max(abs(sl.orthop[0:2]), kmax) ;Axis with smallest variation
if kmax ne 0 then z[points] = v + byte(kmax * sl.nc3) $ ;Shade it
else z[points] = v
zb[points] = z1[points] ;Update depth buffer
done: tv, z ;Now show it
tv, zb, /WORDS, CHANNEL=1 ;and update depth buffer
widget_control, sl.pos_text, set_value = $
STRING(z0, az, el, FORMAT="('(',3f5.1,') A=', i4, ' E=',i4)")
slicer_show
end
PRO slicer_draw, ax, slice ;draw a slice.
; ax = axis, 0 for x, 1 for Y, 2 for Z. slice = plane number.
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
WIDGET_CONTROL, sl.base, /HOURGLASS
SLICER_JOURNAL, "TRANS", [sl.trans, sl.threshold * 100. / sl.nc3]
SLICER_JOURNAL, "SLICE", [ax, slice, sl.interp, sl.expose, 0.]
set_plot,'Z'
z_last = tvrd() ;save previous image & z
zb_last = tvrd(CHANNEL=1, /WORDS)
d0 = [0,0,0]
d1 = dims -1
d0[ax] = slice
d1[ax] = slice
if sl.expose THEN erase ;Get a clean slice
;extract & scale the slice
offset = byte(ax*sl.nc3) ;bias for this slice
p = bytscl(a[d0[0]:d1[0], d0[1]:d1[1], d0[2]:d1[2]], $
max=sl.amax, min=sl.amin, top = sl.nc3-1)
if sl.trans THEN t = (sl.threshold > 1) + offset $ ;lower limit
ELSE t = 0
if ax ne 0 THEN p = p + offset ;add bias
d1 = dims-1
s = replicate(slice, 4)
case ax of
0: polyfill, s, [0,d1[1],d1[1],0],[0,0,d1[2],d1[2]],/T3D,$
pat=reform(p, dims[1], dims[2], /OVER), $
image_coord = [0,0, d1[1],0, d1[1],d1[2], 0,d1[2]], $
image_interp= sl.interp, trans=t
1: polyfill, [0,d1[0],d1[0],0],s,[0,0,d1[2],d1[2]],/T3D,$
pat=reform(p, dims[0], dims[2], /OVER), $
image_coord = [0,0, d1[0],0, d1[0],d1[2], 0,d1[2]], $
image_interp= sl.interp, trans=t
2: polyfill,[0,d1[0],d1[0],0],[0,0,d1[1],d1[1]],s,/T3D,$
pat=reform(p, dims[0], dims[1], /OVER),$
image_coord = [0,0, d1[0],0, d1[0],d1[1], 0,d1[1]], $
image_interp= sl.interp, trans=t
ENDCASE
if sl.expose THEN BEGIN
z = tvrd(/WORDS, CHANNEL=1) ;The new slice
pnts = where((z gt zb_last) + (z eq z[0,0])) ;where we display prev
if n_elements(pnts) gt 1 then begin
z[pnts] = zb_last[pnts] ;New Z
tv, z, /WORDS, CHANNEL=1
z = tvrd() ;New display
z[pnts] = z_last[pnts]
tv, z
ENDIF
ENDIF
slicer_show
end
PRO slicer_colors, table ;load our color table
; Table = index of color table to load, -1 to retain present.
; The color palette is repeated 3 times, once for each of the possible face
; directions. The colors indices:
; 3 * sl.nc3 = red
; 3 * sl.nc3 + 1 = green
; 3 * sl.nc3 + 2 = blue
; 3 * sl.nc3 + 3 = white
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
common colors, r_orig, g_orig, b_orig, r_curr, g_curr, b_curr
v = [0, .5, 1.] ;Shading of the planes.
if n_params() eq 0 then table = -1
if table ge 0 then loadct, /SILENT, table
SLICER_JOURNAL, "COLOR", [table, sl.stretch, 100. * sl.shading]
t = sl.stretch /100. * sl.nc3
if t[1] eq t[0] then s = sl.nc3 else s = sl.nc3 / (t[1]-t[0])
q = long(3*s*(findgen(sl.nc3) - t[0]))
r_curr = bytarr(3*sl.nc3)
g_curr = r_curr
b_curr = r_curr
s = 1.-sl.shading
for i=0,2 do begin ;3 faces
v0 = sl.shading * v[i] * 255
r_curr[i*sl.nc3] = s * r_orig[q] + v0
g_curr[i*sl.nc3] = s * g_orig[q] + v0
b_curr[i*sl.nc3] = s * b_orig[q] + v0
endfor
tvlct, r_curr, g_curr, b_curr
; load last 4 colors as red, green, blue, white
tvlct, [255,0,0,255],[0,255,0,255],[0,0,255,255],3*sl.nc3
end
PRO DO_ISOSURFACE ;Draw the isosurface
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
WIDGET_CONTROL, sl.base, /HOURGLASS
SLICER_JOURNAL, "ISO", [(sl.isop.value-sl.amin)/(sl.amax-sl.amin)*100, $
sl.isop.hi_lo]
set_plot,'Z'
widget_control, sl.pos_text, set_value='Computing Polygons'
shade_volume, a, sl.isop.value, verts, polys, $
low = sl.isop.hi_lo
if n_elements(verts) eq 0 then begin
widget_control, sl.pos_text, set_value = $
'No surface at this value'
set_plot,sl.gdev
endif else begin
widget_control, sl.pos_text, set_value = $
strtrim((size(verts))[2],2)+' Vertices, ' + $
strtrim((size(polys))[1]/4,2) + ' Polygons.'
z_last = tvrd() ;Save old display
zb_last = tvrd(CHANNEL=1, /WORDS)
SET_SHADING, Values=[0, sl.nc3-1]
b = polyshade(verts,polys,/T3D,top=sl.nc3-1)
verts = 0 & polys = 0 ;Free space
slicer_show
endelse
end
PRO draw_cube, c0, c1, faces, color
; draw a cube whose opposite corners are [c0[0],c0(1),c0(2)],
; and [c1[0], c1(1), p2(3)].
; color = drawing color.
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
p0 = intarr(3,8)
p1 = float(p0)
cc = [[c0],[c1]]
for i=0,7 do BEGIN
p0[0,i] = [ cc[0, i and 1], cc[1, (i/2 and 1)], cc[2, (i/4 and 1)]]
p1[0,i] = convert_coord(p0[*,i], /T3D,/TO_DEVICE,/DATA)
ENDFOR
if n_elements(color) le 0 THEN color = sl.nc1
f = sl.facevs
flags = bytarr(8,8) ;line flags, dont draw same line twice
for i=0,n_elements(faces)-1 do BEGIN
ff = [ f[*,faces[i]], f[0,faces[i]]] ;Vertex indices
for j=0,3 do begin
k = ff[j] < ff[j+1] & l = ff[j] > ff[j+1]
if not flags[k,l] then plots, p1[*,[k,l]], color = color, /dev
flags[k,l] = 1
ENDFOR
ENDFOR
end
PRO slicer_erase ;draw the background
; call with no params to erase all.
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
SLICER_JOURNAL, "ERASE"
set_plot,'Z'
erase
sl.p1 = convert_coord(sl.p0, /T3D, /TO_DEVICE, /DATA) ;save dev coords
s = strarr(8)
for i=0,7 do BEGIN
s[i] = string(sl.p0[*,i],format ="(' (',i0,',',i0,',',i0,')')")
ENDFOR
junk = max(sl.p1[2,*], j)
sl.v_close = j ;index of closest vertex
p = where(sl.facevs eq sl.v_close)/4 ;indices of closest verts
colors = [ sl.nc1, sl.nc3*3+1] ;white, green
for i=0,5 do BEGIN ;draw faces
k= (where(p eq i))[0] lt 0 ;1 = not close, 0 = close
draw_cube, [0,0,0], dims-1, i, colors[k]
ENDFOR
for i=0,7 do xyouts, sl.p1[0,i],sl.p1[1,i],/DEVICE,s[i], $
color=colors[i ne sl.v_close]
z_last = tvrd()
zb_last = tvrd(CHANNEL=1, /WORDS)
slicer_show
end
Function slicer_plane_int, dummy
; Return the intersections of the plane with the volume cube.
; V[3,12] = intersections of plane with the 12 edges.
; Flags[12] = 1 if there is an intersection at that edge.
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
Jn = sl.orthop[0:2] ;Plane eqn
Jd = sl.orthop[3]
v = fltarr(3,12)
k = 0
for i=0,11 do begin ;Faces
p0 = sl.p0[*,sl.edges[0,i]] ;Beginning of edge
p1 = sl.p0[*,sl.edges[1,i]] ;End of edge
lv = p1-p0
t = total(lv * Jn)
if t ne 0 then begin
t = -(jd + total(p0 * Jn)) / t
p = p0 + t * lv ;Point of intersection
d = min((p - p0) * (p1-p))
if d ge 0.0 then begin ;Within edge?
v[0,k] = p ;Yes, save intersection
k = k + 1
endif
endif ;T ne 0
endfor ;i
; Sort into order:
d = max(abs(Jn), m) ;Largest plane coeff = what we ignore
u = v[[m+1, m+2] mod 3, 0:k-1] ;Get other 2 coords
a = fltarr(k) ;Angle measure
d = min(u[1,*], dmin) ;Get lowest point
;p0 = u[*,dmi])
;for i=0,k-1 do if i ne dmin then begin ;Get angles
; d = u[*,i] - p0 ;Dx,dy
; d0 = total(abs(d)) ;abs(dx) + abs(dy)
; if d0 ne 0. then begin
; t = d(1) / d0 ;Proportional to angle w. horizontal
; if d[0] lt 0. then t = 2. - t $
; else if d(1) lt 0. then t = t + 4
; endif else t = 0.
; a[i] = t
; endif
u_dx = u[0,*]-u[0,dmin]
u_dy = u[1,*]-u[1,dmin]
zero_ind = Where((u_dx EQ 0.0) AND (u_dy EQ 0.0))
if (zero_ind[0] GE 0L) THEN u_dx[zero_ind] = 1.0
a=atan(u_dy, u_dx)
zero_ind=0 & u_dx=0 & u_dy=0
a[dmin] = -100. ;Anchor = first
n = sort(a) ;Go around anchor point & back
return, v[*, [n, n[0]]]
end
PRO slicer_show, image
;move the Z buffer to the X display. leave device set to X.
; if parameter is present, show it rather than reading the Z buffer
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
if n_params() eq 0 then begin
set_plot,'Z'
image = tvrd()
endif
set_plot,sl.gdev
wset, sl.window
tv, image
sl.cube_on = 0
end
PRO draw_orientation ;draw the orientation cube in the small window
; Draw the outline of the 3 frontmost faces of the main cube.
; Draw the fixed axis plane in green.
; If the mode is cut or cube, draw the selected cube. Draw the back faces
; in blue, and label the selection points.
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
COMMON slicer_common1, old_slice, d0, z0, az, el
i = sl.mode_bases[mode]
widget_control, i, get_uvalue = draw ;the widget id
widget_control, draw[0], get_value=window ;the window number
wset, window
device, set_graph = 3 ;Copy mode
kc = 3 * sl.nc3 + 1 ;Drawing color
erase
if mode le 0 then begin ;Single slice?
if sl.ortho eq 0 then begin ;Orthogonal?
mark_oblique, kc
goto, done
endif
z = [0,0,0]
d1 = dims-1
endif else begin ;Block.
z = sl.p0cube[*,0] < sl.p0cube[*,1]
d1 = sl.p0cube[*,0] > sl.p0cube[*,1]
endelse
; draw fixed plane:
nlines = 6
p = z
d = (d1-z) / float(nlines-1)
p[fixed] = (d1[fixed] + z[fixed])/2.
d[fixed] = 0.0
for i=0,nlines-1 do BEGIN ;draw fixed direction
xx = replicate(p[0],2)
yy = replicate(p[1],2)
zz = replicate(p[2],2)
if fixed ne 0 THEN plots, [z[0],d1[0]], yy,zz, /T3D, COLOR=kc, /DATA
if fixed ne 1 THEN plots, xx, [z[1],d1[1]],zz, /T3D, COLOR=kc, /DATA
if fixed ne 2 THEN plots, xx, yy, [z[2],d1[2]],/T3D, COLOR=kc, /DATA
p = p + d
ENDFOR
if mode ne 0 then begin ;Do cube
draw_cube, sl.p0cube[*,0], sl.p0cube[*,1], indgen(6), kc+1 ;all faces
;Close faces in white
draw_cube, sl.p0cube[*,0], sl.p0cube[*,1], $
where(sl.facevs eq sl.v_close)/4
for i=0,1 do begin ;Label them
p = convert_coord(sl.p0cube[*,i], /T3D, /TO_DEV, /DATA)
xyouts, p[0], p[1], strtrim(i,2), /device
endfor
endif
done:
draw_cube, [0,0,0], dims-1, where(sl.facevs eq sl.v_close)/4 ;draw close faces
wset, sl.window
return
end
PRO mark_oblique, color ;Draw an oblique slice
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
COMMON slicer_common1, old_slice, d0, z0, az, el
if n_elements(d0) lt 3 then return
plots, d0, /T3D, COLOR=color, /DATA
z1 = [[z0], [z0]]
for i=0,2 do begin
z2 = z1
z2[i,0] = 0. & z2[i,1] = dims[i]-1.
plots, z2, /T3D, COLOR = color, /DATA
endfor
return
end
PRO mark_slice, ev ;mark a horizontal or vertical slice
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
COMMON slicer_common1, old_slice, d0, z0, az, el
; loop until the mouse is released.
oldbuttons = sl.lbuttons
sl.lbuttons = oldbuttons and (not ev.release) or (ev.press)
press = ev.press and 1 ;New left button press?
p = float([ev.x, ev.y]) ;device coords of mouse
if (n_elements(old_slice) le 0) or (ev.press ne 0) then old_slice = -1
kslice = -1 ;assume no face
kc = sl.xcol ;XOR drawing color
if sl.lbuttons and 1 THEN BEGIN ;Marking a face?
f = where(sl.facevs eq sl.v_close)/4 ;3 faces to check that are front
d1 = dims-1
z1 = intarr(3)
d = ''
for i=0,2 do BEGIN ;find the face
j = f[i] ;face index
p1 = sl.p1[0:1, sl.facevs[*,j]] ;vertices
if p_inside_poly(p1, p) THEN BEGIN
face = j
p = p / [ !d.x_size, !d.y_size] ;Get data coords. normalized
face_dim = face mod 3 ;Fixed coord along face
if face le 2 THEN k = 0 ELSE k = dims[face_dim]-1
p = COORD2to3(p[0], p[1], face_dim, k, sl.pt_inverse)
ip = fix(p + 0.5) ;round
if ip[fixed] ge dims[fixed] then goto, NO_BREAK ;Outside
kslice = ip[fixed] ;slice number
d1[fixed] = kslice
z1[fixed] = kslice
goto, NO_BREAK
ENDIF
ENDFOR
NO_BREAK:
ENDIF ;Marking a face
device, SET_GRAPH=6 ;set XOR mode
valid = kslice ge 0
if sl.ortho eq 0 then begin ;Oblique???
if n_elements(az) eq 0 then begin ;Initialize?
az = 0. & el = 0. & z0 = dims / 2.
sl.orthop = [ 0., 0., 1., -total(dims)/2]
endif
el1 = el & az1 = az & z1 = z0 ;Old params
if (sl.lbuttons and 4) ne 0 then begin ;New azimuth/elev?
if sl.oangle then $
el1 = fix((p[0]/!d.x_size * 198) - 99.) > (-90) < 90 $
else az1 = fix((p[0]/!d.x_size * 396) - 198.) > (-180) < 180
if (el1 ne el) or (az1 ne az) then begin
s = sin(el1 * !dtor)
sl.orthop[0] = sin(-az1 * !dtor) * s
sl.orthop[1] = cos(-az1 * !dtor) * s
sl.orthop[2] = cos(el1 * !dtor)
valid = 1
endif
endif else if valid then begin ;New origin?
p[face_dim] = z0[face_dim] ;New coords, 1 remains unchanged
z1 = p
valid = total(abs(z0-z1)) ne 0. ;Change...
ENDIF
if (valid) then begin ;Draw new
sl.orthop[3] = -total(sl.orthop * z0)
IF old_slice gt 0 THEN mark_oblique, kc ;Erase old
old_slice = 0
el = el1 & az = az1 & z0 = z1 ;New parameters
if valid and (sl.lbuttons ne 0) THEN BEGIN ;Update obliq outline
WIDGET_CONTROL, sl.pos_text, set_value = $
STRING(z0, az, el, FORMAT="('(',3f5.1,') A=', i4, ' E=',i4)")
d0 = slicer_plane_int() ;New intersections
mark_oblique, kc ;Draw new
old_slice = 1 ;show its visible
ENDIF
ENDIF ;Valid
if ev.release ne 0 then begin ;Released button?
if old_slice then mark_oblique, kc
draw_orientation ;Draw resulting plane
old_slice = 0
endif
ENDIF ELSE BEGIN ;Orthogonal.
IF (kslice ne old_slice) THEN BEGIN ;Movement?
if (press eq 0) and (old_slice ge 0)then $ ;Erase old?
draw_cube, z0, d0, fixed, kc
if valid then $
d = 'Position: ' + string(ip, format="('(',i4,',',i4,',',i4,')')") $
else d = ''
WIDGET_CONTROL, sl.pos_text, set_value=d
if valid and sl.lbuttons THEN BEGIN
draw_cube, z1, d1, fixed, kc
z0 = z1
d0 = d1
ENDIF
IF (ev.release eq 1) and (old_slice ge 0) THEN BEGIN
device, SET_GRAPH=3
slicer_draw, fixed, old_slice ;Mark the slice
ENDIF ;Release
old_slice = kslice ;Save current position
ENDIF ;Movement
ENDELSE
device, SET_GRAPH=3 ;normal mode
end
PRO do_cube ;Draw a cube or cut
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
WIDGET_CONTROL, sl.base, /HOURGLASS
SET_PLOT, 'Z'
z = (z_last = tvrd()) ;Save previous image & current cont
zb = (zb_last = tvrd(CHANNEL=1, /WORDS))
SLICER_JOURNAL, "TRANS", [sl.trans, sl.threshold * 100. / sl.nc3]
SLICER_JOURNAL, "CUBE", [ mode, sl.cut_ovr, sl.interp, reform(sl.p0cube, 6) ]
d0 = sl.p0cube[*,0] < sl.p0cube[*,1] ;Lower corner
d1 = (sl.p0cube[*,0] > sl.p0cube[*,1]) -d0
coords = fltarr(3,8)
for i=0,7 do coords[0,i] = $ ;Verts of our cube
[ (i and 1) * d1[0], (i and 2)/2 *d1[1], (i and 4)/4 * d1[2]] + d0
faces = where(sl.facevs eq sl.v_close)/4 ;close faces
relation = mode eq 1
FOR face = 0, 5 do $ ;Draw the faces of the cube or cut
IF ((where(face eq faces))[0] ge 0) eq relation then begin ;Do this face?
v = 0 & ones = 0 & z0 = 0 & q = 0 ;Clear things out
erase ;Reset Z buffer
verts = sl.facevs[*,face] ;Vertices
polyfill, coords[*, verts], /T3D ;Draw polygon for face
z0 = tvrd(CHANNEL=1, /WORDS) ;Now read the Z buffer
if mode eq 1 then $ ;Cube? or Cut?
points = where(z0 gt zb) $ ;New points for cube
else if sl.cut_ovr then points = where(z0 ne z0[0,0]) $ ;Cut mode?
else points = where((z0 ne z0[0,0] and (zb gt z0))) ;Over mode
widget_control, sl.pos_text, set_value = $
'Creating face ' + strtrim(face,2)
if points[0] ne -1 THEN BEGIN ;Anything to do?
; Get volume coords of plane:
v = p_inverse(points mod !d.x_size, points / !d.x_size, z0[points])
; Either interpolate or pick nearest neighbor
widget_control, sl.pos_text, set_value = $
(['Sampling','Interpolating'])[sl.interp] + $
' face ' + strtrim(face,2) + ', ' + $
strtrim(n_elements(v)/3,2) + ' Pixels'
if sl.interp THEN v = interpolate(a, v[*,0], v[*,1], v[*,2]) $
else begin
v = round(v)
v = a[long(temporary(v)) # [1, dims[0], dims[0] * dims[1]]]
endelse
;Update our points
if sl.trans THEN BEGIN ;Transparency?
good = where(v ge sl.threshold, count)
if count le 0 then goto, skipit
v = v[good]
points = points[good]
endif
offset = byte((face mod 3) * sl.nc3) ;Offset
q = bytscl(v, max=sl.amax, min=sl.amin, top = sl.nc3-1) ;face data
;Get subscripts in data cube
if offset ne 0 then q = q + offset
z[points] = q ;Store the new face
zb[points] = z0[points] ;in both buffers
skipit:
ENDIF ;Anything to do
ENDIF ;Each face
tv, z ;Now show it
tv, zb, /WORDS, CHANNEL=1 ;and update depth buffer
v = 0 & ones = 0 & z0 = 0 & q = 0 ;Clear things out
z = 0 & zb = 0
widget_control, sl.pos_text, set_value = 'Done.'
slicer_show
end
PRO mark_cube1, p0, ip ;Draw the outline of the cube in the main drawable
; p0 = cube coordinates (3,2).
; ip = index of corner that is marked (0 or 1).
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
draw_cube, p0[*,0],p0[*,1], indgen(6), sl.xcol ;Basic cube
p1 = [[p0[*,ip]], [p0[*,ip]]]
p = sl.p0[*,sl.v_close]
for i=0,2 do begin ;Lines to faces
p1[i,0] = p[i]
plots, p1, color=128, /T3D
p1[i,0] = p1[i,1]
endfor
end
PRO mark_cube, ev ;Mark a cube in the main window
; Use the XOR graphics mode to avoid killing the display
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
if n_params() eq 0 then begin ;Erase old cube?
device, SET_GRAPHICS = 6 ;Set XOR mode
if sl.cube_on then mark_cube1, sl.p0cube, sl.cube_ip
sl.cube_on = 0
device, SET_GRAPHICS=3
return
ENDIF
oldbuttons = sl.lbuttons
sl.lbuttons = oldbuttons and (not ev.release) or (ev.press)
press = ev.press and 1 ;New left button press?
if sl.lbuttons eq 0 then begin ;Released all buttons?
if oldbuttons ne 0 then draw_orientation ;Update viewbox
return
ENDIF
x = ev.x / float(!d.x_size) ;Normalized coords
y = ev.y / float(!d.y_size)
d1 = dims -1
p0cube = sl.p0cube ;Save old coords
cube_ip = sl.cube_ip
sl.cube_ip = sl.lbuttons eq 2 ;Corner index
q = sl.p0cube[fixed, sl.cube_ip] ;Fixed axis/point
p = fix(COORD2TO3(x, y, fixed, q, pti)+0.5) ;3D coords
sl.p0cube[*,sl.cube_ip] = p < d1 > 0 ;New corner value
sl.p0cube[fixed, sl.cube_ip] = q
if (total(abs(sl.p0cube - p0cube)) eq 0.0) and $ ;No change?
cube_ip eq sl.cube_ip then return
device, SET_GRAPHICS = 6 ;Set XOR mode
if sl.cube_on then mark_cube1, p0cube, cube_ip ;Erase prev
sl.cube_on = 1
mark_cube1, sl.p0cube, sl.cube_ip
d = string(sl.p0cube, format="('(', 3i4, ') (', 3i4, ')')")
widget_control, sl.pos_text, set_value = d ;label it
DEVICE, SET_GRAPHICS = 3 ;Restore
end
PRO slicer_event, ev
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
swin = !d.window
wset, sl.window ;Our window
if ev.id eq sl.draw THEN BEGIN ;mouse press?
IF mode le 2 THEN BEGIN ;Slice or cube mode?
IF ((ev.press and 4) ne 0) and sl.ortho THEN BEGIN ;Right but= chg plane?
fixed = (fixed + 1) mod 3 ;bump plane
draw_orientation
goto, clean_exit
ENDIF
if mode eq 0 then BEGIN ;Slice mode?
if (ev.press and 2) ne 0 then goto, probe_it ;Middle = probe
i = sl.lbuttons or ev.press ;New state
if ((sl.ortho eq 0) and (i ne 0)) or (sl.ortho and i) THEN BEGIN
mark_slice, ev ;mark the slice
goto, clean_exit
endif
ENDIF ELSE if mode le 2 THEN BEGIN
mark_cube, ev ;Move the cube
ENDIF
goto, clean_exit
ENDIF ELSE BEGIN ;Other modes
if ev.press eq 0 then goto, clean_exit
probe_it: if (n_elements(zb_last) le 1) THEN goto, clean_exit
set_plot,'Z'
z = tvrd(ev.x, ev.y, /WORD, /CHANNEL) ;Z-buffer value at x,y
set_plot,sl.gdev
d = 'No Data Value'
if z[0] gt -32760 THEN BEGIN ;anything there?
p = p_inverse(ev.x, ev.y, z[0]) ;To object coords
p = round(p) ;round
p = p > 0 < (dims-1) ;to range
x = a[p[0], p[1], p[2]] + 0
y = fix(100.*(x - sl.amin)/(sl.amax - sl.amin)) ;To %
d = 'Position: '+string(p,format="('(',i0,',',i0,',',i0,')')") + $
', Data= ' + strtrim(x,2) + ' (' + strtrim(y,2) + '%)'
ENDIF ;Something there
widget_control, sl.pos_text, set_value = d
ENDELSE
goto, clean_exit
ENDIF ;Drawable window
if ev.id eq sl.isop.drawable then begin ;Isosurface threshold
if ev.press eq 0 then goto, clean_exit
x = (ev.x - sl.isop.xs[0]) / sl.isop.xs[1]
x = x > sl.amin < sl.amax
WIDGET_CONTROL, sl.isop.slider, $
SET_VALUE = 100.*(x - sl.amin) / (sl.amax-sl.amin)
sl.isop.value = x
goto, clean_exit
ENDIF
if ev.id eq sl.file_text[0] then $
goto, clean_exit ;Ignore return in file name widget
if ev.id eq sl.rslide[2] then BEGIN
slicer_orientation
goto, clean_exit
ENDIF
; here, it must be a button or a slider:
widget_control, ev.id, get_uvalue = eventval
case eventval of
"CANCUBE" : mark_cube ;Undraw outline
"COLORS" : slicer_colors, (where(sl.color_button eq ev.id))[0]
"CUTINTO": sl.cut_ovr = 0
"CUTOVER": sl.cut_ovr = 1
"ERASE" : slicer_erase
"EXIT" : BEGIN
widget_control, ev.top, /DESTROY
if sl.rbase ne 0 then WIDGET_CONTROL, sl.rbase, /DESTROY
z_last = 0
zb_last = 0
goto, close_journal
ENDCASE
"EXPOSE0": sl.expose = 0
"EXPOSE1": sl.expose = 1
"GOCUBE" : do_cube
"HELP" : BEGIN
xdisplayfile, filepath("slicer.txt", subdir=['help', 'widget']), $
title = "Slicer help", $
group = ev.top, $
width = 72, height = 24
goto, clean_exit
ENDCASE
"INTERP0": BEGIN
sl.interp = 0
goto, set_interp
ENDCASE
"INTERP1": BEGIN
sl.interp = 1
set_interp: i = WIDGET_INFO(ev.id, /parent)
WIDGET_CONTROL, i, GET_UVALUE = i ;Buttons
WIDGET_CONTROL, i[1-sl.interp], /SENS
WIDGET_CONTROL, i[sl.interp], SENS=0
ENDCASE
"ORTHO0": BEGIN ;On oblique
sl.ortho = 0
WIDGET_CONTROL, sl.obuttons, MAP=1
draw_orientation
ENDCASE
"ORTHO1": BEGIN
sl.ortho = 1
WIDGET_CONTROL, sl.obuttons, MAP=0
draw_orientation
ENDCASE
"AZIM" : sl.oangle = 0
"ELEV" : sl.oangle = 1
"GOOBL" : BEGIN ;Do an oblique slice
WIDGET_CONTROL, ev.top, /HOURGLASS
slicer_oblique
end
"ORIENTATION": SLICER_ORIENTATION, (where(sl.ori_butt eq ev.id))[0]
"PLAYBACK" : BEGIN
WIDGET_CONTROL, sl.file_text[0], GET_VALUE=name
name = strtrim(name[0],2)
SLICER_PLAYBACK, FILE = name
ENDCASE
"RECORD" : BEGIN
start_journal: if sl.journal ne 0 then free_lun, sl.journal ;Close old
sl.journal = 0
WIDGET_CONTROL, sl.file_text[0], GET_VALUE=name
name = strtrim(name[0])
openw, i, name, ERROR=j, /GET_LUN
if j ne 0 then begin ;OK?
widget_control, sl.file_text[1], set_value = !ERR_STRING
goto, clean_exit
ENDIF
widget_control, sl.file_text[1], set_value = 'Journal Active'
sl.journal = i
ENDCASE
"RECORDOFF" : BEGIN
widget_control, sl.file_text[1], set_value = 'Journal Closed'
close_journal: if sl.journal ne 0 then free_lun, sl.journal ;Close old
sl.journal = 0
ENDCASE
"THRESHOLD" : BEGIN
sl.threshold = sl.nc3 * ev.value / 100.
sl.trans = (ev.value ge 1) and (ev.value le 99) ;On if reasonable
ENDCASE
"HIGH": sl.isop.hi_lo = 0
"LOW" : sl.isop.hi_lo = 1
"ISOSLIDE" : sl.isop.value = (ev.value / 100.)*(sl.amax-sl.amin) + sl.amin
"GO": do_isosurface
"SHADING": BEGIN
sl.shading = ev.value /100.
slicer_colors
ENDCASE
"STMAX": BEGIN
sl.stretch[1] = ev.value
slicer_colors
ENDCASE
"STMIN": BEGIN
sl.stretch[0] = ev.value
slicer_colors
ENDCASE
"UNDO" : slicer_undo
"XROTATION": BEGIN
sl.rotation[0] = ev.value
slicer_orientation
ENDCASE
"ZROTATION": BEGIN
sl.rotation[1] = ev.value
slicer_orientation
ENDCASE
ELSE : BEGIN ;mode button?
k = where(eventval eq sl.mode_names, count) ;Match with mode name?
if count eq 1 THEN BEGIN ;switch mode
if sl.cube_on then mark_cube ;Remove the cube if vis
fixed = 0 ;Reset fixed direction
if sl.mode_bases[mode] ne 0 THEN $ ;Remove panel if mapped
WIDGET_CONTROL, sl.mode_bases[mode], MAP=0
mode = k[0] ;New mode
if sl.mode_bases[mode] ne 0 THEN $ ;Map new base
WIDGET_CONTROL, sl.mode_bases[mode], MAP=1
if mode le 2 THEN BEGIN ;Slice or cube?
draw_orientation
ENDIF
if mode eq 3 THEN BEGIN ;Draw histogram
WSET, sl.isop.window
type = size(a)
int = type[type[0] + 1] le 3 ;True if int type
j = (sl.amax -sl.amin)/100. ;bin size
if int then j = j > 1
h = histogram(a, max=sl.amax, min = sl.amin, bin=j)
if int THEN j = fix(j + .99)
k = sort(h)
n = n_elements(h)
x = findgen(n) * j + sl.amin < sl.amax
xsave = !x.s & ysave = !y.s
PLOT,x,h, xst = 9, yst=8, ymargin=[2,0], $
yrange= [0,h[k[n-8]]], yticks=1, chars=.75, $
xticks=4
sl.isop.xs = !x.s * !d.x_size
WSET, sl.window
!x.s = xsave & !y.s = ysave
ENDIF ;Isosurface
widget_control, sl.obuttons, $ ;Oblique controls
MAP = (mode eq 0) and (sl.ortho eq 0)
goto, clean_exit
ENDIF
print,'Unknown event: ', eventval
help, /STRUCT, ev
ENDCASE
ENDCASE
clean_exit:
wset, swin
end
PRO slicer, GROUP = group, RANGE = range, COMMANDS = commands, $
CMD_FILE = cmd_file, RESOLUTION = resolution, DETACHED = detached, $
MODAL = modal
;+
; NAME:
; SLICER
;
; PURPOSE:
; Widget based application to show 3D volume slices and isosurfaces.
;
; CATEGORY:
; Volume display / rendering.
;
; CALLING SEQUENCE:
; COMMON VOLUME_DATA, A
; A = your_volume_data
; SLICER
;
; INPUTS:
; Variable A in VOLUME_DATA common contains volume data. See EXAMPLE
; section below.
;
; KEYWORD PARAMETERS:
; COMMANDS: An optional string array of commands to execute
; before entering the interactive mode. Commands are
; in the form of a keyword optionally followed one or more
; numeric, blank-separated parameters. For example:
; "COMMAND P1 P2 P3 ... Pn"
; Keywords and parameters are:
; UNDO: Undo previous operation.
; ORI X_Axis Y_Axis Z_axis X_Rev Y_Rev Z_Rev X_Rot Z_Rot Asp
; This command sets the orientation for the SLICER
; display. X_Axis, Y_Axis, and Z_Axis should be 0 for
; data x, 1 for data y, and 2 for data z.
; X_Rev, Y_Rev, and Z_Rev should be 0 for normal, 1 for
; reversed. Asp is the Z axis aspect ratio w/ respect
; to X, Y. X_Rot and Z_Rot are the rotations of the
; X and Z axes in degrees (30 is the default).
; For example, to interchange the X and Z axes and
; reverse the Y use the string:
; ORI 2 1 0 0 1 0 30 30
; TRANS On_Off Threshold: Use this command to turn transparency
; on or off and set the transparency threshold value.
; 1 means on, 0 means off. Threshold is expressed in
; percent of data range (0 = min data value, 100 = max
; data value).
; SLICE Axis Value Interp 0: Draw an orthogonal slice along
; the given axis (0=x, 1=y, 2=z) at Value. Set Interp
; equal to 1 for interpolation, 0 for nearest neighbor.
; Expose = 1 to remove, 0 for normal slice.
; SLICE Azimuth, Elev, Interp, Expose, 1, x0, y0, z0: Draw
; an oblique slice. The oblique plane crosses the
; XY plane at angle Azimuth, with an elevation of Elev.
; It passes thru the point (x0, y0, z0).
; COLOR Table_Index Low High Shading: Set the color tables.
; Table_Index is the pre-defined color table number (see
; LOADCT), or -1 to retain the present table. Low, High
; and Shading are expressed in percent.
; ISO Threshold Hi_Lo: Draw an iso-surface. Threshold is the
; isosurface threshold value. Hi_Lo should be set to 1
; to view the low side, 0 for the high side.
; ERASE: Erase the display.
; CUBE Mode Cut_Ovr Interp X0 Y0 Z0 X1 Y1 Z1: Draw cube
; (mode = 1) or cut-out (mode = 0).
; Cut_Ovr should be set to 1 for cut-over, 0 for
; cut-thru. Interp should be 1 for interpolation, 0
; for nearest neighbor. (X0,Y0,Z0) is the lower corner
; of the cube. (X1,Y1,Z1) is the upper corner.
; (X0 < X1, etc.)
; WAIT Secs: Wait the designated time (in seconds).
;
; CMD_FILE: A string that contains the name of a file containing SLICER
; commands to execute as described above.
;
; DETACHED: if set, put the drawable in a separate window. (Good
; for large windows.)
; GROUP: The base ID of the widget that calls SLICER. When this
; keyword is specified, the death of the caller results in the
; death of the SLICER.
;
; RANGE: A two-element array containing minimum and maximum data
; values of interest. If this keyword is omitted, the data is
; scanned for the minimum and maximum.
; MODAL: If set, then the slicer runs in modal mode.
;
; RESOLUTION: a two element vector giving the width and height of
; the drawing window. Default = 55% by 44% of screen width.
; OUTPUTS:
; No explicit outputs.
;
; COMMON BLOCKS:
; COMMON VOLUME_DATA, A ;Used to pass in the volume data.
; COMMON SLICER_COMMON ;Used internally.
; COMMON SLICER_COMMON1 ;Used internally.
;
; SIDE EFFECTS:
; On exit, the Z-buffer contains the most recent image generated by
; SLICER. The image may be redisplayed on a different device by
; reading the Z-buffer contents, plus the current color table.
; Widgets are created on the X window display.
;
; RESTRICTIONS:
; Widgets are required.
; The volume data must fit in memory.
;
; PROCEDURE:
; The slicer program has the following modes:
; Slices: Displays orthogonal slices thru the data volume.
; Block: Displaces the surfaces of a selected block inside
; the volume.
; Cutout: Cuts blocks from previously drawn objects.
; Isosurface: Draws an isosurface contour.
; Probe: Displays the position and value of objects
; using the mouse.
; Colors: Manipulates the color tables and contrast.
; Rotations: Sets the orientation of the display.
;
; EXAMPLE:
; Data is transferred to the SLICER via the VOLUME_DATA common block
; instead of as an argument. This technique is used because volume
; datasets can be very large and hence, the duplication that occurs when
; passing values as arguments is a waste of memory. Suppose that you
; want to read some data from the file "head.dat" into IDL for use
; in the SLICER. Before you read the data, establish the VOLUME_DATA
; common block with the command:
;
; COMMON VOLUME_DATA, VOL
;
; The VOLUME_DATA common block has just one variable in it. The variable
; can have any name. Here, we're using the name "VOL". Now read the
; data from the file into VOL. For example:
;
; OPENR, 1, "head.dat"
; VOL = FLTARR(20, 30, 42)
; READU, 1, VOL
; CLOSE, 1
;
; Now you can run the SLICER widget application by entering:
;
; SLICER
;
; The data stored in VOL is the data being worked on by the SLICER.
;
; To obtain the image in the slicer window after slicer is finished:
; (Use the image with the current color tables).
;
; SET_PLOT, 'Z' ;Use the Z buffer graphics device
; a = TVRD() ;Read the image
;
; MODIFICATION HISTORY:
; DMS - RSI, Oct, 1991.
; DMS - RSI, Mar, 1992. Added Journaling and expose mode.
; Fixed bug with 24 bit color.
; DMS - RSI, Jan, 1993. Added oblique slices.
; bmh - 10/14/93 - The following minor bug fixes.
; When no elements are found during an iso-surface display,
; an error message was displayed to an invalid device name.
; When no oblique slices are selected, the slicer_oblique
; should return.
; DJC - RSI, Jun, 1994. Fixed oblique slice initialization and
; atan(0,0) problem (on HP).
; DJC - RSI, Feb, 1995. Added modal keyword.
; DJC - RSI, Feb, 1995. Changed "poly" variable to "polyv" to
; avoid clash with math "poly" function.
; DJC - RSI, Mar, 1995. Fixed shading values for iso-surface.
;-
COMMON volume_data, a
COMMON slicer_common, dims, sl, z_last, zb_last, mode, fixed
COMMON slicer_common1, old_slice, d0, z0, az, el
swin = !d.window
mode = 0
fixed = 0
sl_width = 240 ;Slider width
mode_names = [ 'Slices', 'Block', 'Cutout', 'Isosurface', 'Probe', 'Colors', $
'Rotations', 'Journal' ]
nmodes = n_elements(mode_names) ;# of modes
isop = { ISOP, hi_lo : 1, value: 0.0, window : 0, drawable : 0L, slider : 0L, $
xs : fltarr(2) }
; Main data structure
sl = { SLICER, base : 0L, $ ;Main base
draw:0L, $ ;Big drawable
window:0, $ ;Big drawable window index
trans:0, $ ;Transparency flag
threshold_slider: 0L, $ ;Threshold slider
threshold:0b, $ ;Transp threshold in pixel values
mode_names : mode_names, $ ;Names of modes
interp: 1, $ ;Interpolation flag
ortho : 1, $ ;TRUE for orthogonal slices
orthop : fltarr(4), $ ;Plane eqn for ortho slices
mode_bases : lonarr(nmodes), $ ;Mode panel bases
nc3: 0, $ ;# of colors per partition (3 of them)
nc1: 0, $ ;# of colors we use
amax : 0.0, $ ;Data max, min
amin : 0.0, $
xcol : 0, $ ;XOR Drawing color
color_button:lonarr(24), $ ;Color table buttons (up to 24)
axex: intarr(3), $ ;TRUE to reverse axis
axrev: intarr(3), $ ;Axis permutations
ori_butt: lonarr(7), $ ;Orientation buttons
draw_butt: lonarr(4), $ ;Slice draw/expose buttons
pos_text : 0L, $ ;Label widget at bottom
rotation: [ 30., 30.], $ ;Current rotations
v_close: 0, $ ;Index of closest vertex
p0 : fltarr(3,8), $ ;Data coords of cube corners
p1 : fltarr(3,8), $ ;Device coords of cube corners
pt_inverse : fltarr(4,4), $ ;Inverse of !P.T
vfaces : intarr(3,8), $ ;Face index vs vertex index
facevs : intarr(4,6), $ ;Vertex index vs faces
edges : intarr(2,12), $ ;Vertices vs edge index
isop: isop, $ ;Isosurface parameters
p0cube : intarr(3,2), $ ;Corner coords of cube selection
cut_ovr : 0, $ ;Cut mode
cube_on : 0, $ ;If cube is on
cube_ip : 0, $ ;Corner of cube
shading : 0.20, $ ;Amount of differential shading
file_text : LONARR(2), $ ;File name text widgets
cslide : LONARR(3), $ ;Color sliders
rslide : LONARR(3), $ ;Rotation sliders, aspect text
journal : 0, $ ;Journal file
stretch : [0,100], $ ;Stretch params
lbuttons : 0, $ ;Last button state
expose : 0, $ ;Sice mode (0=slice, 1=expose)
gdev : !D.NAME, $ ;Graphics device
obuttons : 0L, $ ;Oblique buttons
oangle : 1, $ ;active angle for oblique
rbase: 0L } ;Drawable base
; Faces vs vertex index
sl.vfaces = [[0,1,2],[1,2,3],[0,2,4],[2,3,4],[0,1,5], [1,3,5], [0,4,5], $
[3,4,5]]
; Vertex indices vs faces (clockwise order).
sl.facevs = [ [0,2,6,4], [0,4,5,1], [2,0,1,3], [1,5,7,3], [3,7,6,2], $
[6,7,5,4]]
;
; vertex numbers vs Edge index (12 edges)
sl.edges = [[0,1],[1,3],[2,3],[0,2], [0,4], [1,5],[2,6],[3,7], $
[4,5],[5,7],[6,7],[4,6]]
if XRegistered("slicer") THEN RETURN
if n_elements(resolution) lt 2 then begin
device, get_screen = resolution
resolution[0] = 5 * resolution[0] / 9
resolution[1] = 4 * resolution[0] / 5
endif
set_plot,'Z'
device, /z_buffering, set_resolution = resolution
set_plot,sl.gdev
z_last = 0
zb_last = 0
s = size(a)
if s[0] ne 3 THEN $
MESSAGE,'Slicer: volume_data common block does not contain 3D data'
dims = s[1:3]
d1 = dims-1
sl.p0cube = [[dims/4], [3 * dims/4]]
for i=0,7 do sl.p0[*,i] = $ ;Data coords of corners
[ (i and 1) * d1[0], (i and 2)/2 * d1[1], (i and 4)/4 * d1[2]]
sl.orthop = [ 0., 0., 1., -dims[2]/2.] ;Initial orthogonal plane
if n_elements(range) ge 2 THEN BEGIN ;Range specified?
sl.amax = range[1]
sl.amin = range[0]
ENDIF ELSE BEGIN
sl.amax = max(a, min = q)
sl.amin = q
ENDELSE
old_slice = 0
az = 0.0
el = 0.0
z0 = [0.0, 0.0, 0.0]
d0 = slicer_plane_int()
sl.base = WIDGET_BASE(TITLE='IDL Slicer', /ROW)
; Setting the managed attribute indicates our intention to put this app
; under the control of XMANAGER, and prevents our draw widgets from
; becoming candidates for becoming the default window on WSET, -1. XMANAGER
; sets this, but doing it here prevents our own WSETs at startup from
; having that problem.
WIDGET_CONTROL, /MANAGED, sl.base
lbase = WIDGET_BASE(sl.base, /COLUMN)
if keyword_set(detached) THEN BEGIN
rbase = WIDGET_BASE(Title='Slicer', EVENT_PRO='SLICER_EVENT')
sl.rbase = rbase
endif else rbase = WIDGET_BASE(sl.base)
sl.obuttons = WIDGET_BASE(rbase, /ROW)
junk = WIDGET_BASE(sl.obuttons, /exclusive, /row)
junk1 = WIDGET_BUTTON(junk, VALUE='Azimuth', UVALUE='AZIM')
junk1 = WIDGET_BUTTON(junk, VALUE='Elevation', UVALUE='ELEV')
WIDGET_CONTROL, junk1, /SET_BUTTON
junk1 = WIDGET_BUTTON(sl.obuttons, VALUE = 'Go', UVALUE='GOOBL')
WIDGET_CONTROL, sl.obuttons, MAP=0 ;Remove buttons for oblique mode
sl.draw = WIDGET_DRAW(rbase, XSIZE=resolution[0], YSIZE=resolution[1],$
RETAIN=2, /BUTTON_EVENTS, /MOTION)
junk = WIDGET_BASE(lbase, COLUMN=3)
junk1 = WIDGET_BUTTON(junk, value="Done", uvalue = "EXIT", /NO_REL)
junk1 = WIDGET_BUTTON(junk, value="Erase", uvalue = "ERASE", /NO_REL)
junk1 = WIDGET_BUTTON(junk, value="Undo", uvalue = "UNDO", /NO_REL)
junk1 = WIDGET_BUTTON(junk, value="Help", uvalue = "HELP", /NO_REL)
junk1 = WIDGET_BUTTON(junk, VALUE='Orientation',/MENU)
ori_names = [ 'X Y Exchange', 'X Z Exchange', 'Y Z Exchange',$
'X Reverse','Y Reverse','Z Reverse', 'Reset']
for i=0,6 do sl.ori_butt[i] = WIDGET_BUTTON(junk1, VALUE=ori_names[i],$
UVALUE = 'ORIENTATION')
widget_control, sl.ori_butt[0], SET_BUTTON=1
junk1 = WIDGET_BUTTON(junk, VALUE='Interpolation',/MENU)
junk2 = WIDGET_BUTTON(junk1, VALUE='Off', UVALUE='INTERP0')
junk3 = WIDGET_BUTTON(junk1, VALUE='On', UVALUE='INTERP1')
WIDGET_CONTROL, junk3, SENS=0 ;Its on now.
widget_control, junk1, set_uvalue=[junk2, junk3]
junk1 = WIDGET_BASE(lbase, /FRAME, COLUMN=3, /EXCLUSIVE)
for i=0,nmodes-1 do $ ; Mode buttons
junk2 = WIDGET_BUTTON(junk1, value=sl.mode_names[i], $
uvalue=sl.mode_names[i], /NO_RELEASE)
junk = WIDGET_BASE(lbase, /FRAME, /COLUMN)
mode_base = WIDGET_BASE(junk) ;For the mode dependent bases
for i=0,nmodes-1 do $
if i ne 2 then sl.mode_bases[i] = WIDGET_BASE(mode_base, uvalue=0L, /COLUMN)
parent = sl.mode_bases[0] ; slices mode
junk = WIDGET_DRAW(parent, XSIZE = sl_width, $
YSIZE = sl_width * float(resolution[1]) / resolution[0])
WIDGET_CONTROL, parent, SET_UVALUE= junk
junk2 = WIDGET_BASE(parent, /ROW)
junk3 = WIDGET_BASE(junk2, /EXCLUSIVE, /ROW)
sl.draw_butt[0] = $
WIDGET_BUTTON(junk3, VALUE = 'Draw', UVALUE='EXPOSE0', /NO_REL)
sl.draw_butt[1] = $
WIDGET_BUTTON(junk3, VALUE = 'Expose', UVALUE='EXPOSE1', /NO_REL)
junk3 = WIDGET_BASE(junk2, /EXCLUSIVE, /ROW)
sl.draw_butt[2] = $
WIDGET_BUTTON(junk3, VALUE = 'Orthogonal', UVALUE='ORTHO1', /NO_REL)
sl.draw_butt[3] = $
WIDGET_BUTTON(junk3, VALUE = 'Oblique', UVALUE='ORTHO0', /NO_REL)
WIDGET_CONTROL, sl.draw_butt[0], /SET_BUTTON
WIDGET_CONTROL, sl.draw_butt[2], /SET_BUTTON
parent = sl.mode_bases[1] ;Cube & Cut modes
junk = WIDGET_BASE(parent, /ROW)
junk1 = WIDGET_BUTTON(junk, value=' GO ', uvalue='GOCUBE', /NO_RELEASE)
junk1 = WIDGET_BUTTON(junk, value=' Cancel ', uvalue='CANCUBE', /NO_REL)
junk1 = WIDGET_BASE(junk, /EXCLUSIVE, /ROW)
junk = WIDGET_BUTTON(junk1, VALUE="Cut Into", UVALUE="CUTINTO", /NO_REL)
junk = WIDGET_BUTTON(junk1, VALUE="Cut Over", UVALUE="CUTOVER", /NO_REL)
junk = WIDGET_DRAW(parent, XSIZE = sl_width, $
YSIZE = sl_width * float(resolution[1]) / resolution[0])
widget_control, parent, set_uvalue= junk
sl.mode_bases[2] = sl.mode_bases[1] ;Cut is copy of cube
parent = sl.mode_bases[3] ; Isosurface mode
junk = widget_button(parent, value='GO', UVALUE='GO')
junk = widget_base(parent, /row)
junk1 = widget_label(junk, value='Display: ')
junk = widget_base(junk, /row, /exclusive)
junk1 = widget_button(junk, value='High Side', uvalue='HIGH', /NO_REL)
junk1 = widget_button(junk, value='Low Side', uvalue='LOW', /NO_REL)
widget_control, junk1, /set_button ;Set low value
sl.isop.slider = WIDGET_SLIDER(parent, xsize=sl_width, MINIMUM = 0, $
UVALUE = "ISOSLIDE", $
TITLE = 'Isosurface Threshold (%)', $
MAXIMUM = 100)
isodraw = WIDGET_DRAW(parent, XSIZE=sl_width, YSIZE = 100, /BUTTON_EVENTS)
; Color tables
parent = sl.mode_bases[5]
junk1 = widget_base(parent, /ROW)
junk = WIDGET_BUTTON(junk1, VALUE = 'Color Tables', /MENU)
junk2 = 0
loadct, get_names = junk2
n = n_elements(junk2) < 24 ;# of buttons to make
FOR i = 0, n-1 DO sl.color_button[i] = $ ;Make color pull down buttons
WIDGET_BUTTON(junk, VALUE=STRTRIM(junk2[i],2), uvalue='COLORS')
sl.cslide[0] = WIDGET_SLIDER(parent, xsize = sl_width, MINIMUM=0, /DRAG, $
MAXIMUM=100, UVALUE = "STMIN", Title="Contrast Minimum", VALUE=0)
sl.cslide[1] = WIDGET_SLIDER(parent, xsize = sl_width, MINIMUM=0, /DRAG, $
MAXIMUM=100, UVALUE = "STMAX", Title="Contrast Maximum", VALUE=100)
sl.cslide[2] = WIDGET_SLIDER(parent, xsize = sl_width, MINIMUM=0, /DRAG, $
MAXIMUM=100, UVALUE = "SHADING", Title="Differential Shading (%)", $
VALUE=20)
parent = sl.mode_bases[6] ;Rotations mode
sl.rslide[0] = WIDGET_SLIDER(parent, xsize=sl_width, MINIMUM=-90, MAXIMUM=90, $
UVALUE = "XROTATION", Title="X Axis Rotation", VALUE=30)
sl.rslide[1] = WIDGET_SLIDER(parent, xsize=sl_width, MINIMUM=-179, $
MAXIMUM=179, UVALUE = "ZROTATION", Title="Z Axis Rotation", VALUE=30)
junk = WIDGET_BASE(parent, /frame, /row)
junk1 = WIDGET_LABEL(junk, VALUE='Z Aspect Ratio:')
sl.rslide[2] = WIDGET_TEXT(junk, VALUE='1.0 ', /EDIT, YSIZE=1, XSIZE=10)
parent = sl.mode_bases[7] ;Journal mode
junk = WIDGET_BASE(parent, /COLUMN)
junk1 = WIDGET_BUTTON(junk, VALUE='Start Recording', UVALUE='RECORD', /NO_REL)
junk1 = WIDGET_BUTTON(junk, VALUE='Stop Recording', UVALUE='RECORDOFF',$
/NO_REL)
junk1 = WIDGET_BUTTON(junk, VALUE='Playback', UVALUE='PLAYBACK', /NO_REL)
junk = WIDGET_BASE(parent, /ROW)
junk1 = WIDGET_LABEL(junk, value='File Name:')
sl.file_text[0] = WIDGET_TEXT(junk, xsize=24, ysize=1, $
value='slicer.jou'+string(replicate(32b,14)), /EDIT, /FRAME)
sl.file_text[1] = WIDGET_TEXT(parent, xsize=32, ysize=1, $
value='Journal Closed', /FRAME)
; Transparency buttons / slider
junk = WIDGET_BASE(lbase, /FRAME, /COLUMN)
sl.threshold_slider = WIDGET_SLIDER(junk, xsize=sl_width, $
MINIMUM=0, MAXIMUM=100,$
UVALUE="THRESHOLD", TITLE="Transparency Threshold (%)", VALUE=0)
junk1 = WIDGET_BASE(lbase, /FRAME) ;Message base
sl.pos_text = WIDGET_TEXT(junk1, xsize=40, ysize=1)
; Unmap mode dependent widgets (Leave journal mapped because of obscure bug)
for i=1, nmodes-1 do widget_control, sl.mode_bases[i], MAP=0
WIDGET_CONTROL, sl.base, /REALIZE
if sl.rbase ne 0 then WIDGET_CONTROL, sl.rbase, /REALIZE
DEVICE, SET_GRAPHICS=3 ;Ensure copy graphics mode
WIDGET_CONTROL, sl.draw, get_value = junk
sl.window = junk ;Main window
WIDGET_CONTROL, isodraw, get_value = junk
sl.isop.window = junk ;Isosurface drawable
sl.isop.drawable = isodraw
sl.nc1 = (!d.n_colors < 256) -1 ;Colors we can use
sl.nc3 = (sl.nc1-3)/3 ;Colors per orientation
slicer_orientation,6 ;Reset to default orientation, erase
slicer_colors, 0
if n_elements(commands) gt 0 then slicer_playback, commands ;Execute cmds?
if n_elements(cmd_file) gt 0 then slicer_playback, file = cmd_file
device, TRANSLATION = tbl ;Read hdw translation table
;Distance between white and black
if !d.name eq 'X' then sl.xcol = tbl[0] xor tbl[sl.nc1] $
else sl.xcol = 196 ;Windows.
tbl=0 ;Kill it
WSET, swin
XManager, "slicer", sl.base, EVENT_HANDLER = slicer_events, GROUP = group, $
/NO_BLOCK, MODAL=KEYWORD_SET(modal)
end